#ifndef TAGGEDSTRING_HPP
#define TAGGEDSTRING_HPP
#include <string>

class TaggedString
	:public std::string
{
public:
	TaggedString();
	TaggedString(const char *str);
	TaggedString(const std::string &str);
	TaggedString(const TaggedString &str);
	~TaggedString();
	
	/// Returns whether this string is "unfinished", meaning it has either an
	/// unmatched quote, or ends with a backslash.
	bool isUnfinished() const;
	bool hasUnfinishedEscapeSeqence() const;
	bool hasUnmatchedQuote() const;
	char getUnmatchedQuote() const;
	
	bool isQuoted(size_t index) const       { return tags[index] & tagQuoteMask; }
	bool isSingleQuoted(size_t index) const { return (tags[index] & tagQuoteMask) == tagSingleQuoted; }
	bool isDoubleQuoted(size_t index) const { return (tags[index] & tagQuoteMask) == tagDoubleQuoted; }
	bool isBackQuoted(size_t index) const   { return (tags[index] & tagQuoteMask) == tagBackQuoted; }
	
	/// Returns true if this character starts or ends a quoted range - that is,
	/// a single quote, double quote, or backquote which is not escaped or
	/// quoted by a different kind of quote.
	bool isQuote(size_t index) const        { return tags[index] & tagQuote; }
	
	/// Returns true if this character is preceded by an odd number of
	/// backslashes and not single-quoted. Note that this is not the same as C
	/// backslash expansion; there are no multicharacter escape sequences (like
	/// '\010'). Characters will be marked as escaped even if the escape
	/// sequence doesn't mean anything (eg, the Q in "\Q" is escaped), but in
	/// that case the backslash itself is not marked as an escape character.
	bool isEscaped(size_t index) const      { return tags[index] & tagEscaped; }
	
	/// Returns whether the given character is an escape character that will be
	/// stripped out by backslash expansion. Doesn't include escaped escapes, like
	/// the second backslash in "\\", and doesn't include the backslashes that lead
	/// meaningless escape sequences, like "\Q".
	bool isEscape(size_t index) const
		{ return tags[index] & tagEscape; }
	
	/// Find the closing quote which matches the opening quote at index. If the
	/// quote is unmatched, returns length(). If there isn't a quote at that
	/// index, or it's an escaped/quoted quote, returns index. If the indicated
	/// quote is actually a closing quote, the result is undefined.
	int findClosingQuote(size_t index);
	
protected:
	enum tag {
		tagQuoted       = 0x01,
		tagEscape       = 0x02,
		tagEscaped      = 0x04,
		tagQuote        = 0x08,
		
		tagQuoteMask = 0x30,
		tagDoubleQuoted = 0x10,
		tagSingleQuoted = 0x20,
		tagBackQuoted   = 0x30,
	};
	
	enum reasonUnfinished {
		notUnfinished=0,
		danglingBackslash,
		danglingSingleQuote,
		danglingDoubleQuote,
		danglingBackQuote,
	};
	
	void buildTags();
	
	unsigned char *tags;
	reasonUnfinished unfinished;
};

#endif
